#include "Easing.h"

/**

Note from Chris Butcher (c.butcher@hotmail.com)

I've added easing for Cubic, Quartic, Quintic, Sinusoidal, Exponential, and Circular easing,
unfortuantely, I wasn't able to test all of these methods yet.

The original formulas, which are from http://www.gizma.com/easing/, have been left as comments
above each easing method for an easy viewing them. The original formulas use these variables

     t = the amount of time that the animation has been running
	d = the duration of the animation
	c = the change in position (from start to finish)
	b = the original starting position (also known as beginning of animation)

Once the formulas have been tested and their integrity established, then you can feel free
to delete all of these comments.

*/

N2F::Iw3D::Easing::Easing()
{
	return;
}

// Move in a straight line at a continuous speed
float N2F::Iw3D::Easing::Linear(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	return roundf((destination - origin) * (((float)currentTime - (float)startTime) / (float)duration) + origin);
}

// Move in a curve at the beginning of the transition.
float N2F::Iw3D::Easing::QuadraticIn(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	float newTime = ((float)currentTime - (float)startTime) / (float)duration;

	return roundf((destination - origin) * newTime * newTime + origin);
}

// Move in a curve towards the end of the transition.
float N2F::Iw3D::Easing::QuadraticOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	float newTime = ((float)currentTime - (float)startTime) / (float)duration;

	return roundf((-(destination - origin)) * newTime * (newTime-2) + origin);
}

// Move in a curve going one direction for the first half, and then switch directions for the second half of the transition.
float N2F::Iw3D::Easing::QuadraticInOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	float newTime = ((float)currentTime - (float)startTime) / ((float)duration / 2);

	if(newTime < 1)
	{
		return roundf((destination - origin) / 2 * newTime * newTime + origin);
	}

	newTime--;

	return roundf((-(destination - origin) / 2) * (newTime * (newTime-2) - 1) + origin);
}

float N2F::Iw3D::Easing::CubicIn(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula
	
	t /= d;
	return c*t*t*t + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(change * newTime * newTime * newTime + origin);
}

float N2F::Iw3D::Easing::CubicOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d;
	t--;
	return c*(t*t*t + 1) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	newTime--;

	return roundf(change * (newTime * newTime * newTime + 1) + origin);
}

float N2F::Iw3D::Easing::CubicInOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d/2;
	if (t < 1) return c/2*t*t*t + b;
	t -= 2;
	return c/2*(t*t*t + 2) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / ((float)duration / 2);
	float change = destination - origin;

	if(newTime < 1)
	{
		return roundf(change / 2 * newTime * newTime * newTime + origin);
	}

	newTime -= 2;

	return roundf(change / 2 * (newTime * newTime * newTime + 2) + origin);
}

float N2F::Iw3D::Easing::QuarticIn(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula
	
	t /= d;
	return c*t*t*t*t + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(change * newTime * newTime * newTime * newTime + origin);
}

float N2F::Iw3D::Easing::QuarticOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d;
	t--;
	return -c * (t*t*t*t - 1) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	newTime--;

	return roundf(-change * (newTime * newTime * newTime * newTime - 1) + origin);
}

float N2F::Iw3D::Easing::QuarticInOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d/2;
	if (t < 1) return c/2*t*t*t*t + b;
	t -= 2;
	return -c/2 * (t*t*t*t - 2) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / ((float)duration / 2);
	float change = destination - origin;

	if(newTime < 1)
	{
		return roundf(change / 2 * newTime * newTime * newTime * newTime + origin);
	}

	newTime -= 2;

	return roundf(-change / 2 * (newTime * newTime * newTime * newTime - 2) + origin);
}

float N2F::Iw3D::Easing::QuinticIn(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula
	
	t /= d;
	return c*t*t*t*t*t + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(change * newTime * newTime * newTime * newTime * newTime + origin);
}

float N2F::Iw3D::Easing::QuinticOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d;
	t--;
	return c*(t*t*t*t*t + 1) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	newTime--;

	return roundf(change * (newTime * newTime * newTime * newTime * newTime + 1) + origin);
}

float N2F::Iw3D::Easing::QuinticInOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d/2;
	if (t < 1) return c/2*t*t*t*t*t + b;
	t -= 2;
	return c/2*(t*t*t*t*t + 2) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / ((float)duration / 2);
	float change = destination - origin;

	if(newTime < 1)
	{
		return roundf(change / 2 * newTime * newTime * newTime * newTime * newTime + origin);
	}

	newTime -= 2;

	return roundf(change / 2 * (newTime * newTime * newTime * newTime * newTime + 2) + origin);
}

float N2F::Iw3D::Easing::SinusoidalIn(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula
	
	return -c * Math.cos(t/d * (Math.PI/2)) + c + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(-change * (float)(std::cos(newTime / duration * (M_PI / 2))) + change + origin);
}

float N2F::Iw3D::Easing::SinusoidalOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	return c * Math.sin(t/d * (Math.PI/2)) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(change * (float)std::sin(newTime / duration * (M_PI / 2)) + origin);
}

float N2F::Iw3D::Easing::SinusoidalInOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / ((float)duration / 2);
	float change = destination - origin;

	return roundf(-change / 2 * (float)(std::cos(M_PI * newTime / duration) - 1) + origin);
}

// Move exponentially through the first half of the transition
float N2F::Iw3D::Easing::ExponentialIn(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:

	return c * Math.pow( 2, 10 * (t/d - 1) ) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(change * std::pow(2, 10 * (newTime / duration - 1)) + origin);
}

// Move exponentially through the second half of the transition.
float N2F::Iw3D::Easing::ExponentialOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	return c * ( -Math.pow( 2, -10 * t/d ) + 1 ) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(change * (- std::pow(2, -10 * newTime/duration) + 1) + origin);
}

// Move exponentialy through the entire transition
float N2F::Iw3D::Easing::ExponentialInOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d/2;
	if (t < 1) return c/2 * Math.pow( 2, 10 * (t - 1) ) + b;
	t--;
	return c/2 * ( -Math.pow( 2, -10 * t) + 2 ) + b;

	*/
	float newTime = ((float)currentTime - (float)startTime) / ((float)duration / 2);
	float change = destination - origin;

	if(newTime < 1)
	{
		return roundf(change / 2 * std::pow(2, 10 * (newTime - 1)) + origin);
	}

	newTime--;

	return roundf(change / 2 * ( - std::pow(2, -10 * newTime) + 2 ) + origin);
}

float N2F::Iw3D::Easing::CircularIn(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d;
	return -c * (Math.sqrt(1 - t*t) - 1) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(-change * (std::sqrt(1 - newTime * newTime) - 1) + origin);
}

float N2F::Iw3D::Easing::CircularOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:
	
	t /= d;
	t--;
	return c * Math.sqrt(1 - t*t) + b;

	*/

	float newTime = ((float)currentTime - (float)startTime) / (float)duration;
	float change = destination - origin;

	return roundf(change * std::sqrt(1 - newTime * newTime) + origin);
}

float N2F::Iw3D::Easing::CircularInOut(int64 currentTime, int64 startTime, float origin, float destination, int64 duration)
{
	/* Original Formula:

	t /= d/2;
	if (t < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
	t -= 2;
	return c/2 * (Math.sqrt(1 - t*t) + 1) + b;

	*/
	float newTime = ((float)currentTime - (float)startTime) / ((float)duration / 2);
	float change = destination - origin;

	if(newTime < 1)
	{
		return roundf(-change / 2 * (std::sqrt(1 - newTime * newTime) - 1) + origin);
	}

	newTime -= 2;

	return roundf(change / 2 * (std::sqrt(1 - newTime * newTime) + 1) + origin);
}

N2F::Iw3D::Easing::~Easing()
{
	return;
}
